#include <QDebug>
#include "qdock.h"
#include "dockwidget.h"
#include "indicators.h"
#include "dockmanager.h"
#include "layoutmanager.h"
#include "previewer.h"
#include "policymanager.h"
#include "containers.h"

UDockWidgetPreviewer::~UDockWidgetPreviewer()
{

}

QPreviewWidget* createBluePreviewWidget(){
    auto ret= new QPreviewWidget(0, Qt::WindowStaysOnTopHint
                                 | Qt::FramelessWindowHint | Qt::Tool);
    ret->setWindowOpacity(0.5);
    QPalette qpal(Qt::darkBlue);
    QColor blue = qpal.highlight().color();
    blue.setAlpha(100);
    qpal.setBrush(QPalette::Window, blue);
    ret->setPalette(qpal);

    ret->setAutoFillBackground(true);
    ret->setFocusPolicy(Qt::NoFocus);
    return ret;
}

QPreviewWidget* createRedPreviewWidget(){
    auto ret= new QPreviewWidget(0, Qt::WindowStaysOnTopHint
                                 | Qt::FramelessWindowHint | Qt::Tool);
    ret->setWindowOpacity(0.5);
    QPalette qpal(Qt::red);
    QColor red( Qt::red);
    red.setAlpha(150);
    qpal.setBrush(QPalette::Window, red);
    ret->setPalette(qpal);

    ret->setAutoFillBackground(true);
    ret->setFocusPolicy(Qt::NoFocus);
    return ret;
}



UDockWidgetPreviewer::UDockWidgetPreviewer():m_target_previewing(0), m_hovering(0)
  ,m_individual_indicators(0)
{
    m_target_previewer = createBluePreviewWidget();
    m_source_previewer = createBluePreviewWidget();

    m_docked_previewer = createRedPreviewWidget();
}


void UDockWidgetPreviewer::previewBasicDocks(QDockItem* dragged_item) {
    auto base_docks = m_dockmanager->layoutManager()->getBasicDocks();
    for (QDockItem* base_dock:base_docks){
        DockOption dockoption = m_dockmanager->policyManager()->dockOptions(dragged_item,base_dock);
        showBaseIndicators(base_dock,dockoption);
    }

}

std::unordered_set<Window> UDockWidgetPreviewer::previewWidgets() const{
    std::unordered_set<Window> ret = std::unordered_set<Window>();
    return ret;
}

void UDockWidgetPreviewer::showDockIndicators(QDockItem* target, DockOption dockoptions){
    //TODO
    if (!m_individual_indicators){
        m_individual_indicators = new IndividualIndicators();
    }
    QList<QRect> obstacles;
    QDockItem* parent = target;
    QDockItem* tmp;
    while (tmp = parent->parentDockItem()){
        parent = tmp;
    }
    for (auto item : m_base_indicators.values(parent)){
        QRect rec = item->rect();
        rec.moveTopLeft(item->mapToGlobal(rec.topLeft()));
        obstacles.append(rec);
    }



    m_individual_indicators->put(target, obstacles, dockoptions);
}


inline QIndicator* makeIndicator(QDockItem *base_dock, DockOption dock_option){
    switch (dock_option){
    case DockOption::Right:
        return new QRightIndicator(base_dock);
    case DockOption::Left:
        return new QLeftIndicator(base_dock);
    case DockOption::Top:
        return new QTopIndicator(base_dock);
    case DockOption::Bottom:
        return new QBottomIndicator(base_dock);
    case DockOption::Tabbed:
        return new QTabbedIndicator(base_dock);
    default:
        return 0;
    }
}

void UDockWidgetPreviewer::addBaseIndicator(QDockItem *base_dock, QIndicator * indicator, int x, int y){
    indicator->move(x,y);
    indicator->raise();
    indicator->show();
    m_base_indicators.insert(base_dock, indicator);
}

int maxEdgeLength(QDockItem* base_dock, QIndicator* indicator){
    int min_base_length = std::min(base_dock->width(), base_dock->height());
    return std::min(min_base_length/3, std::max(indicator->width(), indicator->height()));
}

void setEdgeLength(QDockItem* base_dock, QIndicator* indicator){
    int edge_length = maxEdgeLength(base_dock, indicator);
    indicator->updatePixmap(edge_length);
}

void UDockWidgetPreviewer::showBaseIndicators(QDockItem *base_dock, DockOption dockoptions)
{
    if (dockoptions == DockOption::NoDock)
        return;
    QIndicator* indicator;
    const QSize size = base_dock->size();

    if (dockoptions & DockOption::Left){
        indicator = makeIndicator(base_dock, DockOption::Left);
        setEdgeLength(base_dock,indicator);
        int halfh = indicator->height()/2;
        addBaseIndicator(base_dock,indicator, 0,size.height()/2-halfh);
    }
    if (dockoptions & DockOption::Right){
        indicator = makeIndicator(base_dock, DockOption::Right);
        setEdgeLength(base_dock,indicator);
        int halfh = indicator->height()/2;
        addBaseIndicator(base_dock,indicator,size.width()-indicator->width(),size.height()/2-halfh);
    }
    if (dockoptions & DockOption::Bottom){
        indicator = makeIndicator(base_dock, DockOption::Bottom);
        setEdgeLength(base_dock,indicator);
        int halfw = indicator->width()/2;
        addBaseIndicator(base_dock,indicator,size.width()/2 - halfw, size.height() - indicator->height());
    }
    if (dockoptions & DockOption::Top){
        indicator = makeIndicator(base_dock, DockOption::Top);
        setEdgeLength(base_dock,indicator);
        int halfw = indicator->width()/2;
        addBaseIndicator(base_dock,indicator,size.width()/2 - halfw, 0);
    }
    if (dockoptions & DockOption::Tabbed){
        indicator = makeIndicator(base_dock, DockOption::Tabbed);
        setEdgeLength(base_dock,indicator);

        addBaseIndicator(base_dock,indicator,
                                              base_dock->width()/2 - indicator->width()/2,
                                              base_dock->height()/2 - indicator->height()/2);
    }
}

void UDockWidgetPreviewer::raiseBaseIndicators()
{
    for (auto indicator:m_base_indicators){
        indicator->raise();
    }
}

void UDockWidgetPreviewer::showDockPreview(QDockItem* dragged_item, QDockItem* target, DockOption dockoption){
//TODO
}

void UDockWidgetPreviewer::moveWidget(QDockItem* widget, const QPoint & point, const QPoint & offset){
    if (widget->parentWidget()){
        widget->hide();
        QPoint widgetpos = point;
        widgetpos -= offset;
        widget->move(widgetpos);
        QDockItem* parent = widget->parentDockItem();
        if (parent){
            QDockItemContainer* container = qobject_cast<QDockItemContainer*>(parent);
            if (container){
                container->removeWidget(widget);
            }
        }
        raiseBaseIndicators();
//        widget->setParent(0);
        widget->show();
        return;
    }
    if (m_target_previewing == 0){
        UDockWidget* dw = dynamic_cast<UDockWidget*>(widget);
        m_target_previewing = dw;
        dw->setWindowOpacity(.5);
    }
    QPoint widgetpos = point;
    widgetpos -= offset;
    widget->move(widgetpos);
}

void UDockWidgetPreviewer::hidePreviews(){
    if (m_hovering){
        m_hovering->setHovered(false);
        m_hovering = 0;
    }
    unHighlightSource();
    unHighlightTarget();
    unHighlightDocked();

    if (m_target_previewing){
        m_target_previewing->setWindowOpacity(1);
        m_target_previewing = 0;

    }
//    auto base_docks = m_dockmanager->layoutManager()->getBasicDocks();
//    for (QDockItem* base_dock:base_docks){
//        QBaseDock* qbase_dock = qobject_cast<QBaseDock*>(base_dock);
//         qbase_dock->hideBaseIndicators();
//    }
    for (auto indicator:m_base_indicators){
        delete indicator;
    }
    m_base_indicators.clear();
    if (m_individual_indicators)
        m_individual_indicators->hide();
    //TODO
}

DockOption UDockWidgetPreviewer::hoveredIndicator(const QPoint &point, QWidget *target)
{
//    QBaseDock* basedock = dynamic_cast<QBaseDock*>(target);
//    if (basedock){
//        DockOption dockoption = basedock->hoveredIndicator(QPoint);
//        int crap = 0;
//        if (dockoption == DockOption::NoDock){
//            if (m_hovering)
//                m_hovering->setHovered(false);
//            m_hovering = 0;
//        }
//        if (dockoption != DockOption::NoDock){
//            QIndicator* indicator = basedock->getIndicator(dockoption);
//            if (m_hovering){
//                if (m_hovering != indicator)
//                    m_hovering->setHovered(false);
    //            }
    //            m_hovering = indicator;
    //            m_hovering->setHovered(true);
    //            return dockoption;
    //        }
    //    }
    QIndicator* hovered = 0;
    if (m_base_indicators.contains(target)){
        for (auto indicator:m_base_indicators.values(target)){
            QRect realrect = indicator->rect();
            realrect.moveTopLeft(indicator->mapToGlobal(realrect.topLeft()));
            if (realrect.contains(point)){
                setHovered(indicator);
                return m_hovering->dockoption();
            }
        }
    }

    //No indicators found
    if (m_hovering){
        m_hovering->setHovered(false);
        m_hovering = 0;
    }
    //TODO: DockWidget indicators
    if (m_individual_indicators
            && m_individual_indicators->currentTarget() == target){
        QIndicator* indicator= m_individual_indicators->hoveredIndicator(point);
        if (indicator){
            setHovered(indicator);
            return indicator->dockoption();
        }
    }
    return DockOption::NoDock;
}

void UDockWidgetPreviewer::setHovered(QIndicator* indicator){

    if (m_hovering){
        if (m_hovering != indicator)
            m_hovering->setHovered(false);
        else {
            return;
        }
    }
    m_hovering = indicator;
    indicator->setHovered(true);
}

void UDockWidgetPreviewer::previewToWidget(QPreviewWidget *previewer, QWidget *target, const QRect &geometry)
{
    QRect real_rect;
    QWidget* real_parent = target;
    if (!geometry.isValid()){
        real_rect = target->rect();
    }
    else {
        QRect real_geometry = geometry;
        QDockItem* dockitem_parent = qobject_cast<QDockItem*>(target);
        while  (dockitem_parent){
            QRect parent_geometry = dockitem_parent->geometry();
            parent_geometry.moveTopLeft(dockitem_parent->mapToGlobal(parent_geometry.topLeft()));
            if (parent_geometry.contains(geometry)){
                break;
            }
            dockitem_parent = dockitem_parent->parentDockItem();
        }
        if (dockitem_parent){
            real_parent = dockitem_parent;
            real_rect = real_geometry;
            real_rect.moveTopLeft(dockitem_parent->mapFromGlobal(real_rect.topLeft()));
        }
        else {
            real_parent = target;
            real_rect = target->rect();
        }
    }
    previewer->setParent(real_parent);
    previewer->setGeometry(real_rect);
//    qDebug() << "real rect: " << real_rect;


    QObjectList children = target->children();
    for (int i = children.count()-1;i>=0;i--){
        QIndicator* indicator = qobject_cast<QIndicator*>(children.at(i));
        if (!indicator)
            continue;
        previewer->stackUnder(indicator);
    }
    previewer->show();
//    previewer->setFocus();
    //    m_target_previewer->raise();
}


void UDockWidgetPreviewer::unHover()
{
    if (m_hovering){
        m_hovering->setHovered(false);
        m_hovering = 0;
    }
    m_target_previewer->setParent(0);
    if (m_individual_indicators)
        m_individual_indicators->hide();
    unHighlightDocked();
}

QPreviewWidget *UDockWidgetPreviewer::getTargetPreviewWidget() {
    return m_target_previewer;
}

QPreviewWidget *UDockWidgetPreviewer::getSourcePreviewWidget() {
    return m_source_previewer;
}

void UDockWidgetPreviewer::highlightTarget(QWidget *target)
{
    previewToWidget(m_target_previewer,target);

}

void UDockWidgetPreviewer::unHighlightTarget()
{
    m_target_previewer->setParent(0);
}


/**
 * @brief UDockWidgetPreviewer::hideTargetDockIndicators
 * @deprecated Not sure if we need this function
 */
void UDockWidgetPreviewer::hideTargetDockIndicators()
{
    m_individual_indicators->hide();
}

void UDockWidgetPreviewer::highlightSource(QWidget *source)
{
    previewToWidget(m_source_previewer,source);
    m_source_previewer->setFocus();
}

void UDockWidgetPreviewer::unHighlightSource()
{
    m_source_previewer->setParent(0);
}

void UDockWidgetPreviewer::highlightDocked(QWidget *target, const QRect &rec)
{
//    QRect adjusted(rec);
//    qDebug() << "highlighting dock at";
//    qDebug() << rec;
//    adjusted.moveTopLeft(target->mapFromGlobal(rec.topLeft()));
    previewToWidget(m_docked_previewer, target, rec);
    //TODO: implement
}

void UDockWidgetPreviewer::unHighlightDocked()
{
    m_docked_previewer->setParent(0);
}
